﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Threading;
using Microsoft.DirectX.AudioVideoPlayback;
using SongSharp.Domain.Objects;
using SongSharp.Domain.Events;
using System.Diagnostics;
using SongSharp.Logging;

namespace SongSharp.Domain.Playing
{
    public delegate void TrackPositionChangedEventHandler(object sender, TrackPositionChangedEventArgs e);
    public delegate void ReachedEndOfTrackEventHandler(object sender, EventArgs e);

    public class PlaybackManager
    {
        private static PlaybackManager ms_instance;
        private ChannelAgent Channel { get; set; }
        private BackgroundWorker SongTracker { get; set; }
        
        private PlaybackManager() 
        {
            Channel = new ChannelAgent();
            State = PlayerState.Stopped;
        }

        public event TrackPositionChangedEventHandler TrackPositionChanged;
        public event ReachedEndOfTrackEventHandler ReachedEndOfTrack;
        protected virtual void OnTrackPositionChanged(TrackPositionChangedEventArgs e)
        {
            if (TrackPositionChanged != null)
            {
                TrackPositionChanged(this, e);
            }
        }

        protected virtual void OnReachedEndOfTrack(object sender, EventArgs e)
        {
            if (ReachedEndOfTrack != null)
            {
                ReachedEndOfTrack(this, e);
            }
        }

        public PlayerState State { get; private set; }
        public enum PlayerState
        {
            Stopped = 0,
            Playing = 1,
            Paused = 2
        }

        public static PlaybackManager Instance 
        {
            get 
            {
                if (ms_instance == null)
                    ms_instance = new PlaybackManager();
                return ms_instance;
            }
        }

        public int Volume
        {
            get 
            {
                return Channel.Volume;
            }
            set 
            {
                Channel.Volume = value;
            }
        }

        public void Play(Track track)
        {
            if (track != null)
            {
                State = PlayerState.Playing;
                Audio audio = new Audio(track.Url, false);
                audio.Play();

                BackgroundWorker channelManager = new BackgroundWorker();
                channelManager.DoWork += new DoWorkEventHandler(ChannelManager_DoWork);
                channelManager.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ChannelManager_EndingTrack);

                channelManager.WorkerSupportsCancellation = true;
                channelManager.RunWorkerAsync(Channel.CurrentIndex);
                //Logger.Log("Started worker for: " + System.IO.Path.GetFileName(track.Url), Logger.LogAction.Playback);
                Channel.Create(channelManager, audio);

                if (SongTracker == null || (SongTracker != null && !SongTracker.CancellationPending))
                {
                    if (SongTracker != null)
                        SongTracker.Dispose();
                    BackgroundWorker songTracker = new BackgroundWorker();
                    songTracker.DoWork += new DoWorkEventHandler(SongTracker_DoWork);
                    songTracker.WorkerSupportsCancellation = true;
                    songTracker.RunWorkerAsync();
                }
            }
        }

        void SongTracker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;
            ChannelAgent.ChannelObject o;

            while (!worker.CancellationPending)
            {
                try
                {
                    o = Channel.CurrentChannel;
                    double percent = o.Audio.CurrentPosition * 100 / o.Audio.Duration;
                    OnTrackPositionChanged(new TrackPositionChangedEventArgs(percent));
                }
                catch (NullReferenceException)
                {
                    break;
                }
                Thread.Sleep(500);
            }
        }

        void ChannelManager_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;
            int previousChannel = int.Parse(e.Argument.ToString());
            Thread.Sleep(ChannelAgent.SWITCH_DELAY_MS);
            Channel.Clean(previousChannel);

            while (!worker.CancellationPending)
            {
                try
                {
                    if (Channel.CurrentChannel.Audio.CurrentPosition + (double)ChannelAgent.SWITCH_DELAY_MS / 1000
                            >= Channel.CurrentChannel.Audio.Duration)
                        return;
                    if (Channel.CurrentChannel.Audio.Duration - Channel.CurrentChannel.Audio.CurrentPosition
                            + (double)ChannelAgent.SWITCH_DELAY_MS / 1000
                            < (double)ChannelAgent.HIGH_CONCENTRATION_TIME_INTERVAL_MS / 1000)
                        Thread.Sleep(1);
                    else
                        Thread.Sleep(ChannelAgent.HIGH_CONCENTRATION_TIME_INTERVAL_MS / 2);
                }
                catch (NullReferenceException)
                {
                    e.Cancel = true;
                    break;
                }
            }
            e.Cancel = true;
            //Logger.Log("Cancelled worker for: " + Channel.GetNext(previousChannel), Logger.LogAction.Playback);
        }

        void ChannelManager_EndingTrack(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled)
            {
                OnReachedEndOfTrack(this, new EventArgs());
            }
        }

        public void Stop()
        {
            if (SongTracker != null)
                SongTracker.CancelAsync();

            //Logger.Log("Stopped", Logger.LogAction.Playback);
            State = PlayerState.Stopped;

            Channel.Clean();
        }

        public void Pause()
        {
            if (Channel.CurrentChannel.IsValid)
            {
                if (State == PlayerState.Playing)
                {
                    //Logger.Log("Paused", Logger.LogAction.Playback);
                    State = PlayerState.Paused;
                    Channel.CurrentChannel.Audio.Pause();
                }
                else if (State == PlayerState.Paused)
                {
                    //Logger.Log("Unpaused", Logger.LogAction.Playback);
                    State = PlayerState.Playing;
                    Channel.CurrentChannel.Audio.Play();
                }
            }
        }

        public void Seek(double percent)
        {
            ChannelAgent.ChannelObject o = Channel.CurrentChannel;
            if (o.IsValid)
            {
                double targetPosition = (o.Audio.Duration * percent / 100) * 10000000;
                o.Audio.SeekCurrentPosition(targetPosition, SeekPositionFlags.AbsolutePositioning);
            }
        }
    }
}
